
/*

Design Pattern Automation Toolkit.
Application to create applications with emphasis on Design patterns.
And support for round trip engineering.
Copyright (C) 2004 Vineeth Neelakant. nvineeth@gmail.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
using System;
using BusinessLayer;
using InfoClasses;
using dpatoolkit.UILayer;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.ComponentModel;
using System.Windows.Forms;
using DesignPatternDefinitions;
using dpatoolkit;
using System.Drawing.Printing;

namespace dpatoolkit.UILayer
{
	/// <summary>
	/// Summary description for ComponentForm.
	/// </summary>
	
	public class ClassDiagram : System.Windows.Forms.Panel
	{
		#region Fields
		private System.ComponentModel.IContainer components;
		private IDesignPatternSubject subject;
		private IUIObject curElement=null;
		//UIELements contains the graphical elements that are displayed
        private IDictionary<string, IUIObject> classUIElements = new Dictionary<string,IUIObject>();

		private DMode dmode = DMode.Normal;
		private Point fromPoint , toPoint=new Point(1,1);
		private Pen relPen = new Pen(SystemBrushes.Highlight);
		private bool firstClick=false;
		private string reltext;
		//the old action taken is save and used by the relation updated handler.
		private ActionTaken oldAction=ActionTaken.None;
		private IUIObject firstClickedUIObject,secondClickedUIObject;
		//in this mode the user can only view the diagram and cannot edit, required by plugin details form.
		private bool viewMode=false;
		private System.Windows.Forms.ToolTip toolTip;
		private ContextMenu contextMenu;
	
		/* Used in mouse move event, optimization instead of 
		 * creating a new point every time
		 * */
		private Point MouseMovePoint = new Point(0,0);

        // Indicates if a resize is in progress;
        private bool resizeInProgress = false;
        // The Initial Size when resize started
        private Point ResizePoint;
        private bool onEastBorder = false;
        private bool onSouthBorder = false;
        private bool onWestBorder = false;
        private bool onNorthBorder = false;


		#endregion

		#region Private enums
		enum DMode { DrawClass,Normal,DrawRelation }
		#endregion
		
		#region Constructor and Dispose
		public ClassDiagram(string CompName , IDesignPatternSubject sub, Control parent)
		{
			if(sub==null)
				throw new ArgumentNullException("The subject cannot be null!");
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();
			subject=sub;
			
			this.Text=CompName;
			this.subject.RegisterDelegates(CompName,
				new AddNewClassObserver(this.AddNewClassEventHandler),
				new ClassNameObserver(ClassNameChangedEventHandler),
				new ClassFieldObserver(this.ClassFieldsUpdatedHandler),
				new ClassMethodObserver(this.ClassMethodsUpdatedHandler),
				new RelationObserver(this.ClassRelationsUpdatedHandler),
				new ReInitialize(this.ReInitializeHandler));
			
			this.Size = new Size(800,800);
            this.BackColor = UIRenderer.CanvasColor;
			this.Dock=DockStyle.None;
			IProjectComponent projComp = this.subject.GetProject().GetComponent(this.Text);
			if (projComp!=null)
				this.Size = projComp.Dimension;
			this.InitContextMenu();

            this.SetStyle(ControlStyles.UserPaint, true);
			this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
			this.SetStyle(ControlStyles.DoubleBuffer,true);

            if (parent == null)
                throw new Exception("parent of class diagram not set");
            parent.SizeChanged += new EventHandler(Parent_SizeChanged);
            parent.BackColor = SystemColors.ControlLight;

            this.Dock = DockStyle.None;
		}

        void Parent_SizeChanged(object sender, EventArgs e)
        {
            this.AdjustLocation();
        }
		
		private void InitContextMenu()
		{
			this.contextMenu = new ContextMenu();
			MenuItem copyToClipMenuItem = new MenuItem("Copy Image to ClipBoard");
			MenuItem printMenuItem = new MenuItem("Print this ClassDiagram");

			copyToClipMenuItem.Click+=new EventHandler(copyToClipMenuItem_Click);
			printMenuItem.Click+=new EventHandler(printMenuItem_Click);

			this.contextMenu.MenuItems.Add(copyToClipMenuItem);
			this.contextMenu.MenuItems.Add(printMenuItem);
		}
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}
		#endregion

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.components = new System.ComponentModel.Container();
            this.toolTip = new System.Windows.Forms.ToolTip(this.components);
            this.SuspendLayout();
            // 
            // toolTip
            // 
            this.toolTip.AutomaticDelay = 0;
            this.toolTip.UseAnimation = false;
            this.toolTip.UseFading = false;
            // 
            // ClassDiagram
            // 
            this.Anchor = System.Windows.Forms.AnchorStyles.None;
            this.AutoScroll = true;
            this.AutoSize = true;
            this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))));
            this.Padding = new System.Windows.Forms.Padding(5);
            this.Size = new System.Drawing.Size(800, 800);
            this.TabStop = true;
            this.Text = "Component Class Diagram";
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.UIComponent_MouseMove);
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.UIComponent_MouseDown);
            this.Resize += new System.EventHandler(this.UIComponent_Resize);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UIComponent_MouseUp);
            this.Move += new System.EventHandler(this.UIComponent_Move);
            this.SizeChanged += new System.EventHandler(this.UIComponent_SizeChanged);
            this.MouseEnter += new System.EventHandler(this.UIComponent_MouseEnter);
            this.ResumeLayout(false);

		}
		#endregion

		#region Helper Methods to create/modify UI elements
	
		public IDesignPatternSubject Subject
		{
			get
			{
				return this.subject;
			}
		}
		public bool ViewMode
		{
			get{return viewMode;}
			set{this.viewMode=(bool)value;}
		}

		private bool SetCurrentUIElement(Point p) 
		{
            this.curElement = this.GetUIObjectAtPoint(p);
            if (this.curElement != null)
                return true;
			return false;
		}

		private IUIObject GetUIObjectAtPoint(Point p)
		{
            foreach (IUIObject uiObj in this.classUIElements.Values)
            {
                if (uiObj.Contains(p))
                    return uiObj;
            }
            return null;
		}
		
		/// <summary>
		/// creates the ui class at the specified point.
		/// </summary>
		/// <param name="cls"></param>
		/// <param name="p"></param>
		public UIClass CreateUIClass(IClass cls,Point p)
		{
			UIClass uiobj = new UIClass(cls,p,this);
			
			this.classUIElements.Add(this.GetUIObjectKey(cls), uiobj);
			uiobj.EndDrag();//just in case, the ui object is beyond the canvas!
			this.Invalidate();
			return uiobj ;
		}

		public void WriteUIDetails()
		{
			foreach(IUIObject uiObj in this.classUIElements.Values)
			{
				this.subject.AddClassUIDetails(uiObj.UIComponent,uiObj.UIName , uiObj.Location);
			}
			
			this.subject.AddComponentUIDetails(this.Text,this.Width,this.Height);
		}

		public int UIElementsCount
		{
			get{ return this.classUIElements.Count;}
		}
        protected void DrawGridLines(Graphics g)
        {
            int minorGridLineInterval = 20;
            int majorGridLineInterval = minorGridLineInterval * 4;
            Pen gridPen;
            if (this.curElement != null)
            {
                if (this.curElement.IsDragging())
                    return;
            }
            for (int i = 0; i < this.Size.Height; i += minorGridLineInterval)
            {
                if (i % majorGridLineInterval == 0)
                {
                    gridPen = UIRenderer.MajorGridLinePen;
                }
                else
                    gridPen = UIRenderer.MinorGridLinePen;
                
                g.DrawLine(gridPen, 0, i, this.Size.Width, i);

            }
            for (int i = 0; i < this.Size.Width; i += minorGridLineInterval)
            {
                if (i % majorGridLineInterval == 0)
                {
                    gridPen = UIRenderer.MajorGridLinePen;
                }
                else
                    gridPen = UIRenderer.MinorGridLinePen;

                g.DrawLine(gridPen, i, 0, i, this.Size.Height);
            }
        }
        public void DrawContents(Graphics g, bool drawBGColor)
		{

            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

            if (this.dmode == DMode.DrawRelation)
                g.DrawLine(UIRenderer.ActiveClassBoundaryPen, this.fromPoint, this.toPoint);

            if (drawBGColor)
            {
                g.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, this.Size.Width, this.Size.Height));
            }

            /** O(n) Drawing Algorithm */

            if( UIRenderer.EnableGridLines)
                this.DrawGridLines(g);

            /* first Ask the UIObjects to compute the space requirements. i.e dimensions */
            foreach (IUIObject element in this.classUIElements.Values)
                    element.ComputeUIRecs(g);
            
            // Draw the Shadows.
            if (UIRenderer.EnableShadows)
            {
                foreach (IUIObject element in this.classUIElements.Values)
                        element.DrawShadow(g);
            }

			/* Then draw all the relations */
            foreach (IUIObject element in this.classUIElements.Values)
			{
					element.DrawRelations(g);
			}

            /* In the last stage draw all the Classes, 
             * so that the lines dont overlap classes / UIObjects */
            foreach (IUIObject element in this.classUIElements.Values)
            {
                    element.Draw(g);
            }
		}

		#region UIHashTable Key Generation
		private string GetHashtableKey(string s1,string s2)
		{
			return s1+"<-->"+s2;
		}
		
		private string GetUIObjectKey(IClass cls)
		{
			return cls.ComponentName+"::"+cls.ClassName;
		}
		private string GetUIObjectKey(IUIObject uiObj)
		{
			return uiObj.UIComponent+"::"+uiObj.UIName;
		}
		
		private string GetUIObjectKey(string comp,string cls)
		{
			return comp+"::"+cls;
		}
		#endregion

		#region UIHashtable object getters, setters		
		public IUIObject GetUIObject( IClass cls)
		{
			string clsKey = this.GetUIObjectKey(cls);
			if(!this.classUIElements.ContainsKey(clsKey) )
			{
				// if a class ui object is not present then create a 
				//remote ui class.. a class that represents a class that belongs to a different class.
				Console.WriteLine("Creating a UIClass which is not present:"+clsKey.ToString());
				this.CreateUIClass(cls,new Point());
				this.classUIElements[clsKey].RemoteObject=true;
			}
			return this.classUIElements[clsKey] as IUIObject;
		}
		public IUIObject GetUIObject( string component,string cls)
		{
			IClass icls = this.subject.GetProject().GetClass(component,cls);
			return this.GetUIObject(icls);
		}

		public void SetUIObject( IClass cls, IUIObject obj)
		{
			this.classUIElements[this.GetUIObjectKey(cls)]=obj;
		}
		#endregion

		private void EndUIRelationState()
		{
			this.dmode=DMode.Normal;
			this.toPoint=this.fromPoint=new Point(0,0);
			this.curElement=null;
			this.oldAction = DPAppData.RequestedAction; //save the old action state, useful for optimization and improve responsiveness.
			DPAppData.RequestedAction = ActionTaken.None;
			this.secondClickedUIObject=this.firstClickedUIObject=null;
			this.Cursor=Cursors.Default;
			this.Invalidate();
			this.firstClick=false;
			return;
		}
		
		public void ClearAll()
		{
			this.classUIElements.Clear();
		}
		
		#endregion

		#region Code To Save as Image
		public void SaveAsImage(string path)
		{
			string fname=path;
			if(path==null || path==string.Empty)
			{
				SaveFileDialog sfd = new SaveFileDialog();
				sfd.AddExtension=true;
				sfd.DefaultExt="png";
				sfd.CheckPathExists=true;
				sfd.Filter="Image Files(*.PNG;*.JPG;*.GIF;*.BMP)|*.PNG;*.JPG;*.GIF;*.BMP";
				if (sfd.ShowDialog()==DialogResult.Cancel)
					return;
				
				fname = sfd.FileName;
				path = fname.ToLower();
			}
			
			
			
			ImageFormat fmt;
			if(path.EndsWith("jpg"))
				fmt=ImageFormat.Jpeg;
			else if(path.EndsWith("gif") )
				fmt=ImageFormat.Gif;
			else if(path.EndsWith("bmp"))
				fmt=ImageFormat.Bmp;
			else 
			{
				fmt=ImageFormat.Png;
				if( !fname.EndsWith(".png"))
				{
					fname+=".png";
					MessageBox.Show("Extension not provided! Image will be saved in png format!");
				}
			}

			this.GetBitmap().Save(fname, fmt);
		}
		public Bitmap GetBitmap()
		{
			Bitmap newBmp = new Bitmap(this.Size.Width,this.Size.Height);
			Graphics g = Graphics.FromImage(newBmp);
			this.DrawContents(g,true);
			return newBmp;
		}
		#endregion

		#region Mouse EventHandlers

		private void UIComponent_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			try
			{
				if(this.viewMode)
					return;

                /* Disable the tooltip */
                this.toolTip.Active = false;

			
				Point p = new Point(e.X,e.Y);
				if ( e.Clicks == 2 && this.SetCurrentUIElement(p)) 
				{
					this.subject.ShowClassEditor(this.curElement.UIComponent,this.curElement.UIName,1);
					this.Invalidate();
					this.curElement=null;
					return;
				}
				if( e.Button==MouseButtons.Middle&& this.SetCurrentUIElement(p)) 
				{
					this.curElement.ShowMinimal=!this.curElement.ShowMinimal;
					this.Invalidate();
					this.curElement=null;
					return;
				}
				if ( e.Button == MouseButtons.Left ) 
				{
                    if (DPAppData.RequestedAction == ActionTaken.None && this.SetCurrentUIElement(p))
                    {
                        // The click was made on a UI Object, Init the drag process;
                        curElement.InitDrag(p);
                        return;
                    }
                    else if ( this.Cursor == Cursors.SizeNS || this.Cursor == Cursors.SizeWE 
                        || this.Cursor == Cursors.SizeAll)
                    {
                        // The User is trying to resize the Draw Area;
                        this.resizeInProgress = true;
                        this.ResizePoint.X = e.X;
                        this.ResizePoint.Y = e.Y;
                    }
				}
			}
			catch
			{
			}
			finally
			{
			}
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			base.OnPaint(e);
			Graphics g = e.Graphics;
			this.DrawContents(g,false);
		}

		private void UIComponent_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			this.ShowHoverSenseMessage();
			
			if(this.viewMode)
				return;

            // The resizing of the drawing canvas is in progress;
            if (this.resizeInProgress)
            {
                // Diagonal Resizing : South East flag
                bool onSouthEast = this.onSouthBorder && this.onEastBorder;
                bool onNorthWest = this.onNorthBorder && this.onWestBorder;
                int newWidth = 100, newHeight = 100;

                if (this.onWestBorder || onNorthWest)
                    newWidth = this.Width + (this.ResizePoint.X - e.X);

                if (this.onNorthBorder || onNorthWest)
                    newHeight = this.Height + (this.ResizePoint.Y - e.Y);

                if (this.onEastBorder || onSouthEast )
                    newWidth = this.Width + (e.X - this.ResizePoint.X);

                if (this.onSouthBorder || onSouthEast)
                    newHeight = this.Height + (e.Y - this.ResizePoint.Y);
                
                if (newWidth > 100)
                {
                    this.Width = newWidth;
                    this.ResizePoint.X = e.X;
                }

                if (newHeight > 100)
                {
                    this.Height = newHeight;
                    this.ResizePoint.Y = e.Y;
                }

                DPAppData.MDIManager.ShowStatusMessage(this.Size.ToString());

                return;
            }

			if (this.firstClick)
			{
				this.toPoint.X=e.X;
				this.toPoint.Y=e.Y;
				this.Invalidate();
				return;
			}

            if (curElement != null && curElement.IsDragging())
            {
                Rectangle r = curElement.BoundingRectangle;
                this.MouseMovePoint.X = e.X;
                this.MouseMovePoint.Y = e.Y;
                curElement.Drag(this.MouseMovePoint, this);
                this.Cursor = Cursors.SizeAll;
            }
            else
            {
                onEastBorder = e.X < this.Width && e.X > (this.Width - 10);
                onSouthBorder = e.Y < this.Height && e.Y > (this.Height - 10);
                onNorthBorder = e.Y < 10;
                onWestBorder = e.X < 10;

                bool onSouthEast = this.onSouthBorder && this.onEastBorder;
                bool onNorthWest = this.onNorthBorder && this.onWestBorder;

                int intersections = Convert.ToInt32(onEastBorder) +
                    Convert.ToInt32(onSouthBorder) + Convert.ToInt32(onNorthBorder)
                    + Convert.ToInt32(onWestBorder);

                if (onEastBorder || onWestBorder)
                {
                    this.Cursor = Cursors.SizeWE;
                }
                else if (onNorthBorder || onSouthBorder)
                {
                    this.Cursor = Cursors.SizeNS;
                }

                if (onSouthEast || onNorthWest)
                    this.Cursor = Cursors.SizeAll;

                if (intersections == 0)
                {
                    if (DPAppData.RequestedAction != ActionTaken.None)
                        this.Cursor = Cursors.Cross;
                    else
                        this.Cursor = Cursors.Default;

                }

            }
		}

		private void UIComponent_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if(this.viewMode)
				return;

			Point p = new Point(e.X,e.Y);

            try
            {
                // When the mouse is lifted, end the resize operation;
                if (this.resizeInProgress)
                {
                    this.resizeInProgress = false;
                    this.AdjustLocation();
                    DPAppData.MDIManager.ResetStatusMessage();
                    // Call End Drag, so that if a class is out of class diagram area, then
                    // it appears withing the diagram;
                    foreach (IUIObject uiObj in this.classUIElements.Values)
                        uiObj.EndDrag();
                    this.Invalidate();
                }

                this.WriteUIDetails();
                if (curElement != null /*&& curElement.IsDragging()*/)
                {
                    curElement.EndDrag();
                    curElement = null;
                    this.Cursor = Cursors.Default;
                    this.Refresh();
                    return;
                }

                if (e.Button == MouseButtons.Left)
                {
                    #region First Click has been done
                    if (this.firstClick)
                    {
                        //find the class on which the mouse was released .
                        this.secondClickedUIObject = this.GetUIObjectAtPoint(p);
                        if (this.secondClickedUIObject == null)
                        {
                            this.EndUIRelationState();
                            return;
                        }
                        if (this.firstClickedUIObject == this.secondClickedUIObject)
                        {
                            MessageBox.Show("Cannot add a relation from a class to itself..please choose 2 different classes");
                            this.EndUIRelationState();
                            return;
                        }

                        ActionTaken request = DPAppData.RequestedAction;
                        ActionTaken rtype = request;

                        if (request == ActionTaken.AddSingleAggretation || request == ActionTaken.AddMultiAggretation ||
                            request == ActionTaken.AddSingleReference || request == ActionTaken.AddMultiReference)
                        {
                            InputForm form = new InputForm("Relation Name", "Relation Name");
                            form.ShowDialog();
                            if (form.DialogResult == DialogResult.Cancel)
                            {
                                this.EndUIRelationState();
                                return;
                            }
                            this.reltext = form.Input;
                        }

                        if (rtype == ActionTaken.AddInheritance)
                        {
                            IInheritance inh = new InfoClasses.InheritanceInfo();
                            inh.BaseClass = this.firstClickedUIObject.UIName;
                            inh.DerivedClass = this.secondClickedUIObject.UIName;
                            inh.BaseClassComponent = this.firstClickedUIObject.UIComponent;
                            inh.DerivedClassComponent = this.secondClickedUIObject.UIComponent;
                            this.subject.AddInheritance(inh, true);
                        }

                        if (rtype == ActionTaken.AddSingleAggretation ||
                            rtype == ActionTaken.AddMultiAggretation)
                        {
                            IAggregate agg = new InfoClasses.AggregateInfo();
                            agg.AggregateeClass = this.firstClickedUIObject.UIName;
                            agg.AggregatorClass = this.secondClickedUIObject.UIName;
                            agg.AggregatorClassComponent = this.secondClickedUIObject.UIComponent;
                            agg.AggregateeClassComponent = this.firstClickedUIObject.UIComponent;
                            agg.AggregateName = this.reltext;
                            if (rtype == ActionTaken.AddSingleAggretation)
                                agg.Order = RelationOrder.Single;
                            else
                                agg.Order = RelationOrder.Multiple;
                            this.subject.AddAggregate(agg);
                        }

                        if (rtype == ActionTaken.AddSingleReference ||
                            rtype == ActionTaken.AddMultiReference)
                        {
                            IReference rf = new InfoClasses.ReferenceInfo();
                            rf.ReferedClassName = this.firstClickedUIObject.UIName;
                            rf.ReferenceClass = this.secondClickedUIObject.UIName;
                            rf.ReferenceComponent = this.secondClickedUIObject.UIComponent;
                            rf.ReferedComponentName = this.firstClickedUIObject.UIComponent;
                            rf.ReferenceName = this.reltext;

                            if (rtype == ActionTaken.AddSingleReference)
                                rf.Order = RelationOrder.Single;
                            else
                                rf.Order = RelationOrder.Multiple;
                            this.subject.AddReference(rf);
                        }

                        if (rtype == ActionTaken.AddSingleCreates ||
                            rtype == ActionTaken.AddMultiCreates)
                        {
                            ICreation cr = new InfoClasses.CreationInfo();
                            cr.CreateeClassName = this.firstClickedUIObject.UIName;
                            cr.CreatorClassName = this.secondClickedUIObject.UIName;
                            cr.CreatorComponentName = this.secondClickedUIObject.UIComponent;
                            cr.CreateeComponentName = this.firstClickedUIObject.UIComponent;
                            if (rtype == ActionTaken.AddSingleCreates)
                                cr.Order = RelationOrder.Single;
                            else
                                cr.Order = RelationOrder.Multiple;
                            this.subject.AddCreates(cr);
                        }

                        this.EndUIRelationState();

                        return;

                    }
                    #endregion

                    switch (DPAppData.RequestedAction)
                    {
                        case ActionTaken.CreateClass:
                            #region CreateClass logic

                            this.CreateNewClass(p);
                            DPAppData.RequestedAction = ActionTaken.None;

                            break;
                            #endregion

                        case ActionTaken.AddSingleAggretation:
                        case ActionTaken.AddMultiAggretation:
                        case ActionTaken.AddInheritance:
                        case ActionTaken.AddSingleReference:
                        case ActionTaken.AddMultiReference:
                        case ActionTaken.AddMultiCreates:
                        case ActionTaken.AddSingleCreates:

                            ActionTaken request = DPAppData.RequestedAction;


                            this.firstClickedUIObject = this.GetUIObjectAtPoint(p);
                            if (this.firstClickedUIObject == null)//the user did not click on a class.
                            {
                                this.EndUIRelationState();
                                return;
                            }
                            this.dmode = DMode.DrawRelation;
                            this.firstClick = true;
                            this.fromPoint = p;
                            return;

                    }
                }
                else
                    #region The user right clicked
                    if (e.Button == MouseButtons.Right)
                    {
                        ContextMenu cmenu = null;
                        foreach (IUIObject uiobj in this.classUIElements.Values)
                        {

                            if ((cmenu = uiobj.CheckRightClicked(p)) != null)
                            {
                                break;
                            }

                        }
                        /* Check if the right click is on a class */

                        if (cmenu == null)
                        {
                            cmenu = this.contextMenu;
                        }
                        cmenu.Show(this, p);
                    }
                    #endregion

                this.WriteUIDetails();
            }
            catch (Exception exp)
            {
                ErrorMessage.Show("An Exception was caught!Please take a snapshot and report this bug! \n  Exception is :" + exp.ToString(), "Error");
                this.EndUIRelationState();

            }
            finally
            {
                //this.toolTip.Active=true;
            }
		}
		#region Helper
		internal UIClass CreateNewClass(Point p)
		{
			ClassNameForm cnform = new ClassNameForm();
			cnform.ShowDialog(this);
			string cname = cnform.ClassName;
			if(cnform.DialogResult==DialogResult.OK && cname.Length>0)
			{
				//if a class was added then we create the corresponding UI element.
				IClass c =new InfoClasses.ClassInfo();
				c.ComponentName=this.Text;
				c.ClassName=cnform.ClassName;
				c.Abstract=cnform.Abstract;
				c.ClassSummary=cnform.Summary;
				c.AccessModifier=cnform.Visibility;

				if(this.subject.AddClass(c))
					return this.CreateUIClass(c,p);
			}
			return null;//unsucessful.
		}
		#endregion
			
		private void ShowHoverSenseMessage()
		{
            if (GlobalSettings.EnableIntellisense == false)
                return;

			try
			{
				if( this.curElement!=null) 
				{
					return;
				}
				Point p = this.PointToClient(Cursor.Position);
                string msg = string.Empty;
				// Initially look in the Classes.
                if( this.SetCurrentUIElement (p) )
				{
					msg = this.curElement.GetIntellisenseInfo(p);
				}

                // If the point did not collide with any of the classes, 
                // look in the relations.
                foreach(IUIObject uiObj in this.classUIElements.Values)
                {
                    // TODO
                    ArrayList relations = uiObj.Relations;
                    foreach (IUIRelation rel in relations)
                    {
                        if (rel.Contains(p))
                        {
                            msg = rel.GetIntellisenseInfo(p);
                        }
                    }
                }

                if (msg != string.Empty)
                {
                    // Display the tooltip. - if its not active, activate it.
                    if (!this.toolTip.Active)
                    {
                        this.toolTip.Active = true;
                    }
                    this.toolTip.SetToolTip(this, msg);
                }
				else 
				{
					this.toolTip.Active=false;
				}
				this.curElement=null;
			}
			catch(Exception exp)
			{
				ErrorMessage.Show(exp.ToString(),"Intellisense Error");
			}
		}

		private void UIComponent_MouseEnter(object sender, System.EventArgs e)
		{
			if(this.viewMode)
				return;
			if( DPAppData.RequestedAction != ActionTaken.None )
				this.Cursor = Cursors.Cross;
			else
				this.Cursor = Cursors.Default;
		}
		
		#endregion

		#region Window Listeners
		private void UIComponent_SizeChanged(object sender, System.EventArgs e)
		{
			this.WriteUIDetails();
			this.Invalidate();
		}

		private void UIComponent_Move(object sender, System.EventArgs e)
		{
			this.Invalidate();
		}

		private void UIComponent_Resize(object sender, System.EventArgs e)
		{
			this.Invalidate();
            this.AdjustLocation();
		}

		#endregion

        public void AdjustLocation()
        {
            if (this.Parent == null) // Happens during creation time.
                return;
            if (this.Width < this.Parent.Width)
            {
                this.Location = new Point((this.Parent.Width - this.Width) / 2, this.Location.Y);
            }
            else
            {
                this.Location = new Point(0, this.Location.Y);
            }
            if (this.Height < this.Parent.Height)
            {
                this.Location = new Point(this.Location.X, (this.Parent.Height - this.Height) / 2);
            }
            else
            {
                this.Location = new Point(this.Location.X, 0);
            }
        }

		#region Subject Event Handlers
		
		private void AddNewClassEventHandler( object info)
		{
			IClass cls = info as IClass;
			this.CreateUIClass(cls,cls.UILocation);//here the ui location is known and will be meaning full!
			this.Refresh();
		}
		private void ReInitializeHandler( object cname)//object is for future use
		{
			this.ClearAll();
			this.Refresh();
		}

		private void ClassNameChangedEventHandler(string oldName,object cls)
		{
			IClass icls = cls as IClass;
			//generate the new key and old key for the class
			string newClsKey = this.GetUIObjectKey(icls);
			string oldClsKey = this.GetUIObjectKey (icls.ComponentName ,oldName);
			if(false == this.classUIElements.ContainsKey(oldClsKey))
				return; //found a key for a class not present!...
			//first check for the change in the class name
			
			if( oldName != icls.ClassName)
			{
				
				this.classUIElements[newClsKey]	=this.classUIElements[oldClsKey];
				//update the name
				((UIClass) this.classUIElements[newClsKey]).ClassName = icls.ClassName;
				this.classUIElements.Remove(oldClsKey);
				oldClsKey=newClsKey;
			}
			//now update the state of the class ( abstract or concrete )
			((UIClass) this.classUIElements[oldClsKey]).Abstract = icls.Abstract;
			this.Invalidate();
		}
		private void ClassFieldsUpdatedHandler(string cname,object cls)
		{
			string clsKey = this.GetUIObjectKey(cls as IClass);
			if(!this.classUIElements.ContainsKey(clsKey))
				return; //found a key for a class not present!...
			((UIClass) this.classUIElements[clsKey]).UpdateFields(cls as IClass);
			this.Invalidate();
		}
		private void ClassMethodsUpdatedHandler(string cname,object cls)
		{
			string clsKey = this.GetUIObjectKey(cls as IClass);
            if (!this.classUIElements.ContainsKey(clsKey))
				return; //found a key for a class not present!...
			((UIClass) this.classUIElements[clsKey]).UpdateMethods(cls as IClass);
			this.Invalidate();
		}

		private void ClassRelationsUpdatedHandler(string cname,object cls)
		{
			this.Invalidate();
			string clsKey = this.GetUIObjectKey(cls as IClass);

			if ( !this.classUIElements.ContainsKey(clsKey))
			{
				//ErrorMessage.Show("Internal DataStructure Error \n In Relation Handler of UIComponent..null found for "+ cname + " class" + " , "+ this.Text,"Please Report this bug!");
				return;
			}
			((UIClass) this.classUIElements[clsKey]).UpdateAllRelations(cls as IClass);
			this.Invalidate();
			return;
		}
		#endregion

		#region ContextMenuHandling
		public ContextMenu ContextMenu2
		{
			get{return this.contextMenu;}
			set{this.contextMenu = value;}
		}
		
		private void copyToClipMenuItem_Click(object sender, EventArgs e)
		{
			Bitmap bitmap = this.GetBitmap();
			Clipboard.SetDataObject(bitmap,true);
			InfoMessage.Show("Class Diagram copied to clipboard","Info");
		}

		private void printMenuItem_Click(object sender, EventArgs e)
		{
			this.Print();
		}

		private void doc_PrintPage(object sender, PrintPageEventArgs e)
		{
			this.DrawContents(e.Graphics,true);
			e.HasMorePages=false;
		}
        public void Print(Graphics g)
        {
            this.DrawContents(g,true);
        }
        public void Print()
		{
			try
            {
                // - print document.
                PrintDocument printDoc = new PrintDocument();
                printDoc.PrintPage += new PrintPageEventHandler(doc_PrintPage);
                printDoc.DocumentName = this.Text;

                PrintDialog printDialog = new PrintDialog();
                // - print dialog.
                printDialog.AllowCurrentPage = true;
                printDialog.AllowPrintToFile = true;
                printDialog.AllowSelection = false;
                printDialog.AllowSomePages = false;
                printDialog.Document = printDoc;
                //printDoc.DefaultPageSettings = print
                // If the result is OK then print the document.
                if (printDialog.ShowDialog() == DialogResult.OK)
                {
                    printDoc.Print();
                }
               

			}
			catch
			{
				ErrorMessage.Show("No Printers were found to print!");
			}
		}
		#endregion

	}
}

